import { _decorator, Component, resources, Node, ImageAsset, Sprite, Vec3, SpriteFrame, Vec2, Prefab, instantiate, UIOpacity } from 'cc';
import { State } from '../Entity/def/State';
import { Global } from '../Global';
import { EntityManager } from '../map/EntityManager';
import { Hero } from './Hero';
const { ccclass, property } = _decorator;

@ccclass('HeroManage')
export class HeroManage extends Component {

    @property({ type: Hero })
    hero: Hero = null

    @property({ type: Prefab })
    point: Prefab = null

    @property({ type: EntityManager })
    entityManager: EntityManager = null

    globalMoveKey: boolean = false

    private _moveState: boolean = false
    private _moveLock: boolean = false
    // 移动28个单位的距离（1,0,2,0,2,0,1）
    private _step: number = 28
    private _moveFlag = { x: 0, y: 0, desc: '' }
    private _time: number = 0.333
    private _currentIndex = { x: 0, y: 0, floor: 0 }
    private _currentTime: number = 0
    private _currentSpeed: number = 0
    private _curPos: Vec3 = new Vec3();
    private _deltaPos: Vec3 = new Vec3(0, 0, 0);
    private _targetPos: Vec3 = new Vec3();

    start() {
        Global.heroManage = this
        this.hero.node.active = false
        this.hero.initHeroState()
        this.buildAddEntityChild()
    }

    resetPos(x: number, y: number, floor: number) {
        this._moveState = false
        this._currentIndex.x = x
        this._currentIndex.y = y
        this._currentIndex.floor = floor
        this.node.setPosition(42 + 28 * x, 42 + 28 * y)
        this.hero.node.active = true
        this.hero.node.getComponent(UIOpacity).opacity = 255
    }

    move(x: number, y: number, desc: string) {
        // 运动状态阻塞
        if (this._moveState || this._moveLock || this.globalMoveKey || Global.key.globalMoveKey) {
            return;
        }
        this._moveLock = true
        setTimeout(() => {
            this._moveLock = false
        }, 333)
        this._moveFlag.x = x
        this._moveFlag.y = y
        this._moveFlag.desc = desc

        // 方块检测
        const state = this.handleTargetBlock(desc);

        // 播放动画
        if (state.move || state.cartoon) {
            this.hero.startAnim(this._moveFlag.desc)
        }

        // 发生位移
        if (!state.move) {
            this.setPos()
            return;
        }
        this._currentTime = 0;
        this._currentSpeed = this._step / this._time;
        this.node.getPosition(this._curPos);
        Vec3.add(this._targetPos, this._curPos, new Vec3(x * this._step, y * this._step, 0));

        this._currentIndex.x += this._moveFlag.x
        this._currentIndex.y += this._moveFlag.y
        this._moveState = true;
    }

    update(deltaTime: number) {
        // 移动的逻辑
        if (this._moveState) {
            this._currentTime += deltaTime;
            if (this._currentTime > this._time) {
                this.node.setPosition(this._targetPos);
                this._moveState = false;
            } else {
                this.node.getPosition(this._curPos);
                this._deltaPos.x = this._currentSpeed * deltaTime * this._moveFlag.x;
                this._deltaPos.y = this._currentSpeed * deltaTime * this._moveFlag.y;
                Vec3.add(this._curPos, this._curPos, this._deltaPos);
                this.node.setPosition(this._curPos);
            }
        }
    }

    /**
     * 检测下一个方块是什么，并且执行逻辑
     * @returns
     */
    handleTargetBlock(dirct: string): State {
        const state = {
            move: false,
            cartoon: true
        }
        const targetIndex = {
            x: this._currentIndex.x + this._moveFlag.x,
            y: this._currentIndex.y + this._moveFlag.y
        }
        if (targetIndex.x < 0 || targetIndex.x > 10) {
            return state
        }
        if (targetIndex.y < 0 || targetIndex.y > 10) {
            return state
        }
        const blocks = this.entityManager.currentFloor.get(this._currentIndex.floor)
        const entity = blocks.get(targetIndex.x + '-' + targetIndex.y)
        if (entity === null || entity === undefined) {
            state.move = true
            return state
        }
        return entity.handleEvent(dirct)
    }

    up() {
        this.move(0, 1, 'up')
    }

    down() {
        this.move(0, -1, 'down')
    }

    left() {
        this.move(-1, 0, 'left')
    }

    right() {
        this.move(1, 0, 'right')
    }

    private leftSF: SpriteFrame = null
    private rightSF: SpriteFrame = null
    private upSF: SpriteFrame = null
    private downSF: SpriteFrame = null

    setPos() {
        if (this._moveFlag.x > 0) {
            this.hero.getComponent(Sprite).spriteFrame = this.rightSF
        } else if (this._moveFlag.x < 0) {
            this.hero.getComponent(Sprite).spriteFrame = this.leftSF
        } else if (this._moveFlag.y > 0) {
            this.hero.getComponent(Sprite).spriteFrame = this.upSF
        } else if (this._moveFlag.y < 0) {
            this.hero.getComponent(Sprite).spriteFrame = this.downSF
        }
    }

    private buildAddEntityChild() {
        resources.load('hero/left', ImageAsset, (err, imageAsset) => {
            if (err) {
                return console.error("ImageAssert load failed, err:" + err.message);
            }
            this.leftSF = SpriteFrame.createWithImage(imageAsset);
        });
        resources.load('hero/right', ImageAsset, (err, imageAsset) => {
            if (err) {
                return console.error("ImageAssert load failed, err:" + err.message);
            }
            this.rightSF = SpriteFrame.createWithImage(imageAsset);
        });
        resources.load('hero/up', ImageAsset, (err, imageAsset) => {
            if (err) {
                return console.error("ImageAssert load failed, err:" + err.message);
            }
            this.upSF = SpriteFrame.createWithImage(imageAsset);
        });
        resources.load('hero/down', ImageAsset, (err, imageAsset) => {
            if (err) {
                return console.error("ImageAssert load failed, err:" + err.message);
            }
            this.downSF = SpriteFrame.createWithImage(imageAsset);
        });
    }

    getDownSF(): SpriteFrame {
        return this.downSF
    }

    getPos() {
        return {
            x: this._currentIndex.x,
            y: this._currentIndex.y,
            floor: this._currentIndex.floor
        }
    }


    quickMove(x: number, y: number, desc: string) {

        this._moveFlag.x = x
        this._moveFlag.y = y
        this._moveFlag.desc = desc

        this.hero.startAnim(this._moveFlag.desc)

        this._currentTime = 0;
        this._currentSpeed = this._step / this._time;
        this.node.getPosition(this._curPos);
        Vec3.add(this._targetPos, this._curPos, new Vec3(x * this._step, y * this._step, 0));

        this._currentIndex.x += this._moveFlag.x
        this._currentIndex.y += this._moveFlag.y
        this._moveState = true;
    }

    private moveInterval: number = undefined

    moveByPath(path: number[][], call: Function) {
        this.globalMoveKey = true
        let step = 0
        this.createPoints(path)
        this.moveInterval = setInterval(() => {
            if (step >= path.length - 1) {
                clearInterval(this.moveInterval)
                call()
                return
            }
            if (!this._moveState) {
                const index = this.getXY(path[step], path[++step])
                this.removePoint(path[step][0], path[step][1])
                if (step >= path.length - 1) {
                    this.globalMoveKey = false
                    this.hero.stopAnim()
                    this.move(index.x, index.y, index.desc)
                } else {
                    this.quickMove(index.x, index.y, index.desc)
                }
            }
        }, 10)
    }

    cancelQuaickMove() {
        this.removePoint()
        clearInterval(this.moveInterval)
        this.globalMoveKey = false
    }

    private getXY(start: number[], end: number[]) {
        const result = {
            x: end[0] - start[0],
            y: end[1] - start[1],
            desc: ''
        }
        result.desc = this.getPosDesc(result.x, result.y)
        return result
    }

    getPosDesc(x: number, y: number) {
        if (x > 0) {
            return 'right'
        } else if (x < 0) {
            return 'left'
        } else if (y > 0) {
            return 'up'
        } else {
            return 'down'
        }
    }

    private _points: Map<string, Node> = new Map()

    private createPoints(path: number[][]) {
        this._points.forEach(node => {
            Global.em.node.removeChild(node)
        })
        this._points = new Map()
        let step = 0
        while (step < path.length - 1) {
            const index = ++step
            const x = path[index][0]
            const y = path[index][1]
            const point = instantiate(this.point)
            point.setPosition(14 + x * 28, 14 + y * 28)
            this._points.set(x + '-' + y, point)
            Global.em.node.addChild(point)
        }
    }

    private removePoint(x?: number, y?: number) {
        if (x === undefined || y === undefined) {
            this._points.forEach(node => {
                Global.em.node.removeChild(node)
            })
            return
        }
        const point = this._points.get(x + '-' + y)
        this._points.delete(x + '-' + y)
        Global.em.node.removeChild(point)
    }
}

